package moltencore.player;

import moltencore.player.exception.PlayerNotLoggedInException;
import moltencore.player.exception.PlayerAlreadyRegisteredException;
import moltencore.player.exception.PlayerNameAlreadyExistsException;

import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Collection;

import spark.network.IPlayerListEventListener;

public class PlayerList<PlayerType extends Player>{
	private HashMap<Long, PlayerType> onlinePlayers;
	private LinkedList<IPlayerListEventListener> authorizationListeners = new LinkedList<IPlayerListEventListener>();
	private boolean anonymousPlayers;

	public PlayerList() {
		this(false);
	}

	public PlayerList(boolean anonymousPlayers) {
		this.anonymousPlayers = anonymousPlayers;
		onlinePlayers = new HashMap<Long, PlayerType>();
	}

	public PlayerList(int initialCapacity) {
		onlinePlayers = new HashMap<Long, PlayerType>(initialCapacity);
	}

	public void login(PlayerType player) throws PlayerNameAlreadyExistsException, PlayerAlreadyRegisteredException {
		//if the session is already in use throw an exception
		if(onlinePlayers.containsKey(player.getSession())) {
			throw new PlayerAlreadyRegisteredException(player.getSession() + "(" + player.getName() + ")");
		}
		//if a name collision would occur don't let the player join
		//unless players are anonymous and we ignore the names
		if(!anonymousPlayers && doesNameCollide(player.getName())) {
			throw new PlayerNameAlreadyExistsException(player.getName());
		}

		onlinePlayers.put(player.getSession(), player);
		notifyAuthorizationListenersAuthorization(player.getSession());
	}

	private void notifyAuthorizationListenersAuthorization(long session) {
		for(IPlayerListEventListener listener : authorizationListeners) {
			listener.loggedIn(session);
		}
	}

	public void logout(long session) throws PlayerNotLoggedInException {
		Player removedPlayer = onlinePlayers.remove(session);
		if(removedPlayer == null) {
			throw new PlayerNotLoggedInException("Player with session " + session + " is not logged in");
		}
		notifyAuthorizationListenersUnauthorization(session);
	}

	private void notifyAuthorizationListenersUnauthorization(long session) {
		for(IPlayerListEventListener listener : authorizationListeners) {
			listener.loggedOut(session);
		}
	}

	private boolean doesNameCollide(String name) {
		for(Player p : onlinePlayers.values()) {
			if(name.equals(p.getName()))
				return true;
		}
		return false;
	}

	public int size() {
		return onlinePlayers.size();
	}

	public Collection<PlayerType> getOnlinePlayers() {
		return onlinePlayers.values();
	}

	public PlayerType getPlayer(long session) {
		return onlinePlayers.get(session);
	}

	public void addAuthorizationListener(IPlayerListEventListener listener) {
		authorizationListeners.add(listener);
	}
}
